package com.gopay.scheduler.job;

import java.math.BigDecimal;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;

import com.gopay.common.autowithdraw.AwCustRuleState;
import com.gopay.common.autowithdraw.dao.AwCustRuleDAO;
import com.gopay.common.constants.rcs.TranDirection;
import com.gopay.common.constants.trans.DrawOpType;
import com.gopay.common.constants.trans.PayChannel;
import com.gopay.common.constants.trans.UserType;
import com.gopay.common.constants.txncd.IntTxnCd;
import com.gopay.common.domain.UserInfo;
import com.gopay.common.domain.acps.model.AcctMastBean;
import com.gopay.common.domain.autowithdraw.AwCustRule;
import com.gopay.common.domain.cps.transfer.Tran00800VO;
import com.gopay.common.domain.poundage.PoundageReq;
import com.gopay.common.domain.rcs.RcsTransLimitStatistics;
import com.gopay.common.domain.report.ReportConsoleBalanceQuery;
import com.gopay.common.exception.GopayException;
import com.gopay.common.order.dao.CommonQueryDAO;
import com.gopay.common.rcs.service.translimit.RcsTransLimitService;
import com.gopay.common.rcs.vo.RcsTransLimitView;
import com.gopay.common.user.dao.UserInfoQueryDAO;
import com.gopay.common.util.DateUtils;
import com.gopay.cron.service.trans.RcsTransLimitClientService;
import com.gopay.cron.web.util.CallCPSUtil;
import com.gopay.remote.acps.service.AcctTransRemoteService;
import com.gopay.remote.cps.poundage.PoundageRemoteService;
import com.gopay.scheduler.GopayBaseJob;
import com.gopay.scheduler.service.reporthandler.AcctBalanceService;

/**
 * 商户自动提现任务
 * 
 * @author ocean
 *
 */
public class AwCustRuleJob extends GopayBaseJob {

    private static final int PAGESIZE = 500;
    // 日志
    private Logger logger = LoggerFactory.getLogger(this.getClass());
    @Resource(name = "commonQueryDAO")
    private CommonQueryDAO commonQueryDAO;

    @Resource(name = "awCustRuleDAO")
    private AwCustRuleDAO awCustRuleDAO;

    @Resource(name = "rcsTransLimitClientService")
    private RcsTransLimitClientService rcsTransLimitClientService;

    @Resource(name = "userInfoQueryDao")
    private UserInfoQueryDAO userInfoQueryDAO;

    @Resource(name = "rcsTransLimitBaseService")
    private RcsTransLimitService rcsTransLimitService;

    @Resource(name = "poundageRemoteClient")
    private PoundageRemoteService poundageRemoteClient;

    @Resource(name = "acctTransRemoteService")
    private AcctTransRemoteService acctTransRemoteService;
    
    @Resource(name="acctBalanceService")
    private AcctBalanceService acctBalanceService;


    // private @Value("#{webProperties['cps.remote.url']}")
    // String cpsUrl;

    private final static BigDecimal VAL_0 = new BigDecimal(0);

    @Override
    public void doJobInternal() {
    	logger.info("--------自动提现AwCustRuleJob  start----");
		Calendar cal = Calendar.getInstance();
		int firstDayOfMonth = cal.get(Calendar.DAY_OF_MONTH);
		cal.set(Calendar.DAY_OF_MONTH, 1);
		cal.add(Calendar.DAY_OF_MONTH, -1);
//		int lastDayOfMonth = cal.get(Calendar.DAY_OF_MONTH);

		// modified begin by zhaoxubing 20160126 航空票务将任务时间挪到了次日0点5分，所以在这做些修改。
		cal.setTimeInMillis(System.currentTimeMillis());
		cal.add(Calendar.DATE, -1);
		int dayOfWeek = cal.get(Calendar.DAY_OF_WEEK);
		int dayOfMonth = cal.get(Calendar.DAY_OF_MONTH);
		// modified end by zhaoxubing 20160126

        String hql = "from AwCustRule m where m.auditStatus=:auditStatus and m.autoWthdrStatus=:autoWthdrStatus ";
        hql += " and ((m.cycleType in ('0','3') ) or ";
        hql += " (m.cycleType='1' and m.cycleValue like :dayOfWeek) or ";
        hql += " (m.cycleType='2' and m.cycleValue=:dayOfMonth) ";
        if ( firstDayOfMonth == 1 ){
            hql += " or (m.cycleType='2' and m.cycleValue='32'))";
        } else {
            hql += ")";
        }

        Map<String, Object> param = new HashMap<String, Object>();
        param.put("auditStatus", AwCustRuleState.AUDIT_STATUS_SUCCESS.value);
        param.put("autoWthdrStatus", AwCustRuleState.AUTO_WTHDR_STATUS_SUCCESS.value);
        param.put("dayOfWeek", "%"+dayOfWeek + "%");
        param.put("dayOfMonth", dayOfMonth + "");

        int i = 1;
        while (true) {
            long _startTimes = System.currentTimeMillis();

            logger.info("开始查找自动提现规则");
            List<AwCustRule> results = awCustRuleDAO.findByHql(hql, param, i, PAGESIZE);
            if (CollectionUtils.isEmpty(results)) {
                logger.info("查找自动提现规则结束 无符合条件记录");
                break;
            }
            logger.info("查找自动提现规则结束 results.size=" + results.size());
            
            logger.info("第" + i + "次发起自动提现开始");

            for (AwCustRule a : results) {
                // 发起自动提现
                autoWithDraw(a);
            }
            logger.info("第" + i + "次自动提现结束 共耗时" + (System.currentTimeMillis() - _startTimes) / 1000 + "秒");
            i++;
        }
        logger.info("--------自动提现AwCustRuleJob  end----");
        
    }

    private void autoWithDraw(AwCustRule a) {
        // 定额
        if ("0".equals(a.getAutoWthdrType())) {
            fixAutoWithDraw(a);
        }
        // 超额
        else {
            variableAutoWithDraw(a);
        }

    }

    private void fixAutoWithDraw(AwCustRule a) {
        logger.info("定额-自动提现 a=" + a);
        
        List<UserInfo> users = userInfoQueryDAO.getByCustId(a.getCustId());
        if (CollectionUtils.isEmpty(users)) {
            return;
        }

        UserInfo userInfo = users.get(0);

        logger.info("定额-开始查询可用余额");
        AcctMastBean account = acctTransRemoteService.getAccount(a.getAcpsAcctMastId());
        logger.info("定额-查询可用余额结束 account=" + account);
        BigDecimal avaAmt = new BigDecimal(account.getAvaAmt());

        if (a.getAutoWthdrAmount().compareTo(avaAmt) == 1) {
            return;
        }

        TranDirection dir = getTranDirection(userInfo);

        logger.info("定额-开始查询方案");
        RcsTransLimitView view = rcsTransLimitClientService.getLimitConfig(a.getAcpsAcctMastId(), dir, IntTxnCd._00800);
        logger.info("定额-查询方案结束 view=" + view);
        try {
            logger.info("定额-开始风控校验");
            boolean checkAmtLimit = rcsTransLimitClientService.checkAmtLimit(view, a.getAutoWthdrAmount());
            logger.info("定额-风控校验结束 checkAmtLimit=" + checkAmtLimit);

            if(!checkAmtLimit){
                logger.info("风控校验不通过，不发起提现申请");
                return;
            }
            Tran00800VO draw = new Tran00800VO();
            draw.setOpType(DrawOpType.APPLY_CONFIRM);
            draw.setUserId(userInfo.getUserId());
            draw.setAcctId(a.getAcpsAcctMastId());
            draw.setTxnAmt(a.getAutoWthdrAmount());
            draw.setBankId(a.getDefaultBankCardId().longValue());
            draw.setAutoDraw(true);

            logger.info("定额-开始发起自动提现 draw=" + draw);
            CallCPSUtil.callByVOTicket(draw);
            logger.info("定额-自动提现发起结束");

        } catch (GopayException e) {
            logger.error("自动提现-定额提现金额出错 AwCustRule.sq=" + a.getSq(), e);
        }
    }

    private void variableAutoWithDraw(AwCustRule a) {
        logger.info("超额-自动提现 a=" + a);
        List<UserInfo> users = userInfoQueryDAO.getByCustId(a.getCustId());
        if (CollectionUtils.isEmpty(users)) {
            return;
        }

        UserInfo userInfo = users.get(0);

        logger.info("开始获取账户信息");
        AcctMastBean account = acctTransRemoteService.getAccount(a.getAcpsAcctMastId());
        logger.info("账户信息获取结束 account=" + account);
        
        logger.info("开始获取账户前一天日终余额");
        // 当前时间
        Date curDate = new Date();
        // 今天
        String today = DateUtils.format(curDate, "yyyyMMdd");
        // 计算昨天
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(curDate);
        calendar.add(Calendar.DAY_OF_MONTH, -1);
        // 昨天
        String yesterday = DateUtils.format(calendar.getTime(), "yyyyMMdd");
        ReportConsoleBalanceQuery acctBalance = acctBalanceService.queryAcctBalanceByAcctNo(yesterday, today, account.getAcctId());
        BigDecimal balAmt = acctBalance.getBalAmt();
        logger.info("账户前一天日终余额balAmt=" + balAmt);
        // 前一天日终可用余额-账户保留余额=提现金额+手续费
        logger.info("获取可以提现的金额(可用余额-账户保留余额=提现金额+手续费)");
        BigDecimal orderAmount = balAmt.subtract(a.getAutoWthdrAmount());
        logger.info("获取可以提现的金额(可用余额-账户保留余额=提现金额+手续费) orderAmount=" + orderAmount);
        
        if(0 >= orderAmount.compareTo(VAL_0)){
            logger.info("提现金额小于等于0，不发起提现");
            return;
        }
        
        a.setAutoWthdrAmount(orderAmount);
        logger.info("可以提现的金额为orderAmount=" + orderAmount + ",a.getAutoWthdrAmount()=" + a.getAutoWthdrAmount());
        // 计算手续费
        logger.info("开始计算手续费");
        BigDecimal poundage = this.getPoundage(a, userInfo);
        logger.info("计算手续费结束 poundage=" + poundage);
        // 设置减去手续费后的提现金额
        a.setAutoWthdrAmount(a.getAutoWthdrAmount().subtract(poundage));
        logger.info("设置减去手续费后的提现金额 a.getAutoWthdrAmount()=" + a.getAutoWthdrAmount());

        TranDirection dir = getTranDirection(userInfo);

        logger.info("开始获取方案");
        RcsTransLimitView view = rcsTransLimitClientService.getLimitConfig(a.getAcpsAcctMastId(), dir, IntTxnCd._00800);
        logger.info("获取方案结束 view=" + view);

        // 校验单次限额
        logger.info("获取单次限额");
        BigDecimal onceLimit = rcsTransLimitClientService.getOnceLimit(view);
        logger.info("单次限额获取结束 onceLimit=" + onceLimit);
        if (null != onceLimit && 0 <= onceLimit.compareTo(VAL_0) ) {
            // 如果大于单次限额，则替换为单次限额
            if (0 < a.getAutoWthdrAmount().compareTo(onceLimit)) {
                logger.info("本次提现金额"+a.getAutoWthdrAmount()+"大于单次限额"+onceLimit);
                a.setAutoWthdrAmount(onceLimit);
                logger.info(" 替换为单次限额 a.getAutoWthdrAmount()=" + a.getAutoWthdrAmount());
            }
        }

        // 校验单日限额
        logger.info("获取单日限额");
        BigDecimal dayLimit = rcsTransLimitClientService.getDayLimit(view);
        logger.info("单日限额获取结束 dayLimit=" + dayLimit);
        logger.info("开始查询累计额度与提现次数");
        RcsTransLimitStatistics rcsGAcctLimitStatistics = rcsTransLimitClientService.findRcsGAcctLimitStatistics(view);
        logger.info("查询累计额度与提现次数结束 rcsGAcctLimitStatistics=" + rcsGAcctLimitStatistics);
        if (null != dayLimit && 0 <= dayLimit.compareTo(VAL_0)) {
            logger.info("开始获取当日已提现金额");
            BigDecimal dayLimitStatcs = (null == rcsGAcctLimitStatistics) ? null : rcsGAcctLimitStatistics.getDayLimitStatcs();
            logger.info("获取当日已提现金额结束 dayLimitStatcs=" + dayLimitStatcs);
            BigDecimal todayAutoWthdrAmount = a.getAutoWthdrAmount().add(dayLimitStatcs);
            if (0 < todayAutoWthdrAmount.compareTo(dayLimit) ) {
                logger.info("当日已提现金额"+dayLimitStatcs+"+本次提现金额"+a.getAutoWthdrAmount()+"大于单日限额"+dayLimit);
                a.setAutoWthdrAmount(dayLimit.subtract(dayLimitStatcs));
                logger.info(" 替换为 单日限额-当日已提现金额 a.getAutoWthdrAmount()=" + a.getAutoWthdrAmount());
            }
        }
        // 校验单日次数
        logger.info("开始获取单日提现次数");
        long dayTimes = rcsTransLimitClientService.getDayTimes(view);
        logger.info("获取单日提现结束 dayTimes=" + dayTimes);
        logger.info("开始获取当日已提现次数");
        int dayTimesStatcs = (null ==rcsGAcctLimitStatistics) ? -2 : rcsGAcctLimitStatistics.getDayTimesStatcs();
        logger.info("获取当日已提现次数 dayTimesStatcs=" + dayTimesStatcs);
        if (0 < dayTimes && dayTimes <= dayTimesStatcs) {
            logger.info("当日已提现次数" +dayTimesStatcs + "大于等于单日提现次数" + dayTimes+"，不发起提现");
            return;
        }

        if (0 >= a.getAutoWthdrAmount().compareTo(VAL_0)) {
            logger.info("提现金额为0，不发起提现");
            return;
        }
        
        //加上手续费再减去
       // a.setAutoWthdrAmount(a.getAutoWthdrAmount().add(poundage));
        
        // 发起提现申请
        try {
            Tran00800VO draw = new Tran00800VO();
            draw.setOpType(DrawOpType.APPLY_CONFIRM);
            draw.setUserId(userInfo.getUserId());
            draw.setAcctId(a.getAcpsAcctMastId());
            draw.setTxnAmt(a.getAutoWthdrAmount());
            draw.setBankId(a.getDefaultBankCardId().longValue());
            draw.setAutoDraw(true);

            logger.info("超额-开始发起提现 draw=" + draw);
            CallCPSUtil.callByVOTicket(draw);
            logger.info("超额-发起提现结束");
        } catch (GopayException e) {
            logger.error("自动提现-超额方式提现出错  AwCustRule.sq=" + a.getSq(), e);
        }
    }

    private TranDirection getTranDirection(UserInfo userInfo) {
        TranDirection dir = TranDirection.TO_B_BANK;
        if (StringUtils.equals(UserType.Person.value, userInfo.getUserType())) {
            dir = TranDirection.TO_C_BANK;
        }
        return dir;
    }

    private BigDecimal getPoundage(AwCustRule a, UserInfo userInfo) {
        String payChannel = this.getPayChannel(userInfo);

        PoundageReq req = new PoundageReq();
        req.setAmount(a.getAutoWthdrAmount());
        req.setAcctId(a.getAcpsAcctMastId());
        req.setIntTxnCd(IntTxnCd._00800);
        req.setPayChannel(payChannel);
        return this.poundageRemoteClient.calculate(req).getOrderRealFeeAmt();
    }

    private String getPayChannel(UserInfo user) {
        String payChannel = PayChannel._52.toString();
        if (StringUtils.equals(UserType.Person.value, user.getUserType())) {
            payChannel = PayChannel._02.toString();
        }
        return payChannel;
    }

}
